package memory

import (
	"context"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/google/uuid"
)

// InMemoryStore implements MemoryStore using in-memory storage
type InMemoryStore struct {
	memories map[string]*MemoryEntry
	typeIdx  map[MemoryType]map[string]bool
	mu       sync.RWMutex
}

// NewInMemoryStore creates a new in-memory store
func NewInMemoryStore() *InMemoryStore {
	return &InMemoryStore{
		memories: make(map[string]*MemoryEntry),
		typeIdx:  make(map[MemoryType]map[string]bool),
	}
}

// Store implements MemoryStore.Store
func (s *InMemoryStore) Store(ctx context.Context, memory *MemoryEntry) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	if memory.ID == "" {
		memory.ID = uuid.New().String()
	}

	memory.UpdatedAt = time.Now()
	if memory.CreatedAt.IsZero() {
		memory.CreatedAt = memory.UpdatedAt
	}

	// Store memory
	s.memories[memory.ID] = memory

	// Update type index
	if _, exists := s.typeIdx[memory.Type]; !exists {
		s.typeIdx[memory.Type] = make(map[string]bool)
	}
	s.typeIdx[memory.Type][memory.ID] = true

	return nil
}

// Get implements MemoryStore.Get
func (s *InMemoryStore) Get(ctx context.Context, id string) (*MemoryEntry, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	memory, exists := s.memories[id]
	if !exists {
		return nil, fmt.Errorf("memory not found: %s", id)
	}

	return memory, nil
}

// Search implements MemoryStore.Search
func (s *InMemoryStore) Search(ctx context.Context, query string, limit int) ([]*MemoryEntry, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	var memories []*MemoryEntry
	for _, memory := range s.memories {
		if len(memories) >= limit {
			break
		}
		if strings.Contains(memory.Content, query) {
			memories = append(memories, memory)
		}
	}

	return memories, nil
}

// SearchByEmbedding implements MemoryStore.SearchByEmbedding
func (s *InMemoryStore) SearchByEmbedding(ctx context.Context, embedding []float32, limit int) ([]*MemoryEntry, error) {
	// In a real implementation, this would use a vector similarity search
	// For now, we'll return an error indicating this is not implemented
	return nil, fmt.Errorf("vector similarity search not implemented")
}

// List implements MemoryStore.List
func (s *InMemoryStore) List(ctx context.Context, filters map[string]interface{}, limit int) ([]*MemoryEntry, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	var memories []*MemoryEntry
	for _, memory := range s.memories {
		if len(memories) >= limit {
			break
		}

		// Apply filters
		match := true
		for k, v := range filters {
			if mv, ok := memory.Metadata[k]; !ok || mv != v {
				match = false
				break
			}
		}

		if match {
			memories = append(memories, memory)
		}
	}

	return memories, nil
}

// ListByType implements MemoryStore.ListByType
func (s *InMemoryStore) ListByType(ctx context.Context, memoryType MemoryType, limit int) ([]*MemoryEntry, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	var memories []*MemoryEntry
	if typeMemories, exists := s.typeIdx[memoryType]; exists {
		for id := range typeMemories {
			if len(memories) >= limit {
				break
			}
			if memory, exists := s.memories[id]; exists {
				memories = append(memories, memory)
			}
		}
	}

	return memories, nil
}

// Delete implements MemoryStore.Delete
func (s *InMemoryStore) Delete(ctx context.Context, id string) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	memory, exists := s.memories[id]
	if !exists {
		return fmt.Errorf("memory not found: %s", id)
	}

	// Remove from type index
	if typeMemories, exists := s.typeIdx[memory.Type]; exists {
		delete(typeMemories, id)
	}

	// Remove memory
	delete(s.memories, id)

	return nil
}

// Clear implements MemoryStore.Clear
func (s *InMemoryStore) Clear(ctx context.Context) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.memories = make(map[string]*MemoryEntry)
	s.typeIdx = make(map[MemoryType]map[string]bool)

	return nil
}

// NewInMemoryManager creates a new in-memory manager
func NewInMemoryManager() MemoryManager {
	return &BaseMemoryManager{
		store:  NewInMemoryStore(),
		config: &MemoryConfig{Type: "inmemory"},
		stats:  &MemoryStats{TypeCounts: make(map[MemoryType]int64)},
	}
}
